// Screen Window
// Simplify screen reinit routine by regret

/* changelog:
 update 7: remove refreshWindow
 update 6: add fake fullscreen
 update 5: add scrnSwitchFull to switch fullscreen, scrnExit will not be called
 update 4: simplify enter/exit fullscreen routine
 update 3: do not delete screen in scrnInit
 update 2: add simpleReinitScrn
 update 1: source cleanup
*/

#include "burner.h"
#include <shellapi.h>
#include "cheat.h"
#include "cartridge.h"
#include "maphkeys.h"
#include "config.h"

// Macros used for handling Window Messages
#define HANDLE_WM_ENTERMENULOOP(hwnd, wParam, lParam, fn)		\
    ((fn)((hwnd), (BOOL)(wParam)), 0L)

#define HANDLE_WM_EXITMENULOOP(hwnd, wParam, lParam, fn)		\
    ((fn)((hwnd), (BOOL)(wParam)), 0L)

#define HANDLE_WM_ENTERSIZEMOVE(hwnd, wParam, lParam, fn)		\
    ((fn)(hwnd), 0L)

#define HANDLE_WM_EXITSIZEMOVE(hwnd, wParam, lParam, fn)		\
    ((fn)(hwnd), 0L)

#define HANDLE_WM_UNINITMENUPOPUP(hwnd, wParam, lParam, fn)		\
	((fn)((hwnd), (HMENU)(wParam), (UINT)LOWORD(lParam), (BOOL)HIWORD(lParam)), 0)

#define HANDLE_WM_SIZING(hwnd, wParam, lParam, fn)				\
	((fn)((hwnd), (wParam), (lParam)))

#define HANDLE_WM_DROPFILES(hwnd, wParam, lParam, fn)			\
	((fn)((hwnd), (wParam)))

// Extra macro used for handling Window Messages
#define HANDLE_MSGB(hwnd, message, fn)							\
    case (message):												\
         HANDLE_##message((hwnd), (wParam), (lParam), (fn));	\
		 break;

RECT systemWorkArea = { 0, 0, 640, 480 };		// Work area on the desktop
int windowPosX = -1, windowPosY = -1;			// Window position

int autoPause = 0;
int savestateSlot = 1;

bool showOnTop = false;
bool fullscreenOnStart = false;
bool fakeFullscreen = false;

static wchar_t* szClass = APP_TITLE;			// Window class name
HWND hScrnWnd = NULL;							// Handle to the screen window
HWND hVideoWnd = NULL;							// Handle to the video window

static bool winMaximised;
static int prevWidth, prevHeight;

int scrnWindowSize = 1;

static bool canDrag = false;
static int dragX, dragY;
static int oldWindowX, oldWindowY;
static int leftButtonX, leftButtonY;
UINT32 mousex, mousey;

bool useDialogs()
{
	if (/*!driverOkay ||*/ !vidFullscreen) {
		return true;
	}
	return false;
}

void setPauseMode(bool pause)
{
	runPause = pause;

	if (pause) {
		audioInterface.blank();
		if (useDialogs()) {
			inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		}
	} else {
		gameInpCheckMouse();
	}
}

int createDatfile()
{
	wchar_t szTitle[MAX_PATH];
	wchar_t szFilter[MAX_PATH];

	_snwprintf(commonChoice, sizearray(commonChoice), APP_TITLE L" v%.20s (%s).dat", appBurnVer, L"clrmamepro");
	_snwprintf(szTitle, sizearray(szTitle), localiseStringEx(IDS_DAT_GENERATE), L"clrmamepro");

	swprintf(szFilter, localiseStringEx(IDS_DISK_ALL_DAT), APP_TITLE);
	memcpy(szFilter + wcslen(szFilter), L" (*.dat)\0*.dat\0\0", 16 * sizeof(wchar_t));

	if (commonFileDialog(hScrnWnd, true, szTitle, szFilter, 0, L"dat")) {
		return 1;
	}
	return createDatFile(commonChoice);
}

// simply reinit screen, added by regret
void simpleReinitScrn(bool reinit)
{
	scrnSize();

	if (reinit) {
		videoInterface.reinit();
	}
}

static int onDisplayChange(HWND, UINT, UINT, UINT)
{
	if (!vidFullscreen) {
		mediaReInit();
	}
	return 0;
}

static int onRButtonDown(HWND hwnd, BOOL bDouble, int, int, UINT)
{
	if (hwnd != hScrnWnd) {
		return 1;
	}

	if (bDouble) {
		if (driverOkay) {
			vidFullscreen = !vidFullscreen;
			scrnSwitchFull();
			return 0;
		}
	} else {
		if (!vidFullscreen && !fakeFullscreen) {
			menuEnabled = !menuEnabled;
			scrnSwitchFull();
			return 0;
		}
	}

	return 1;
}

/*
static int onRButtonUp(HWND, int, int, UINT)
{
	return 1;
}
*/

static int onLButtonDown(HWND hwnd, BOOL, int x, int y, UINT)
{
	if (hwnd == hScrnWnd && !vidFullscreen) {
		RECT clientRect;

		GetWindowRect(hwnd, &clientRect);

		oldWindowX = clientRect.left;
		oldWindowY = clientRect.top;

		leftButtonX = clientRect.left + x;
		leftButtonY = clientRect.top + y;

		canDrag = true;
		return 0;
	}

	return 1;
}

static int onLButtonDblClk(HWND hwnd, BOOL, int, int, UINT)
{
	if (hwnd == hScrnWnd && driverOkay) {
		vidFullscreen = !vidFullscreen;
		scrnSwitchFull();
		return 0;
	}
	return 1;
}

static int onMouseMove(HWND hwnd, int x, int y, UINT keyIndicators)
{
	mousex = x;
	mousey = y;

	if (canDrag && hwnd == hScrnWnd && keyIndicators == MK_LBUTTON && !vidFullscreen && !fakeFullscreen) {
		RECT clientRect;
		GetWindowRect(hwnd, &clientRect);

		if ((leftButtonX - (clientRect.left + x)) < dragX
			&& (leftButtonX - (clientRect.left + x)) > -dragX
			&& (leftButtonY - (clientRect.top + y)) < dragY
			&& (leftButtonY - (clientRect.top + y)) > -dragY) {
			SetWindowPos(hwnd, NULL, oldWindowX, oldWindowY, 0, 0, SWP_NOREPOSITION | SWP_NOSIZE);
		} else {
			windowPosX = oldWindowX - (leftButtonX - (clientRect.left + x));
			windowPosY = oldWindowY - (leftButtonY - (clientRect.top + y));

			SetWindowPos(hwnd, NULL, windowPosX, windowPosY, 0, 0, SWP_NOREPOSITION | SWP_NOSIZE);
		}

		return 0;
	}

	return 1;
}

static int onLButtonUp(HWND hwnd, int x, int y, UINT)
{
	canDrag = false;

	if (vidFullscreen) {
		if (hwnd != hScrnWnd) {
			return 1;
		}

		if (useDialogs()) {
			RECT clientRect;
			GetWindowRect(hwnd, &clientRect);
			int clientx = clientRect.left + x;
			int clienty = clientRect.top + y;

			TrackPopupMenuEx(hMenuPopup, TPM_LEFTALIGN | TPM_TOPALIGN, clientx, clienty, hwnd, NULL);
			return 0;
		}
	} else {
		if (!menuEnabled) {
			RECT clientRect;
			GetWindowRect(hwnd, &clientRect);
			int clientx = clientRect.left + x;
			int clienty = clientRect.top + y;

			if ((leftButtonX - clientx) < dragX && (leftButtonX - clientx) > -dragX
				&& (leftButtonY - clienty) < dragY && (leftButtonY - clienty) > -dragY) {
				TrackPopupMenuEx(hMenuPopup, TPM_LEFTALIGN | TPM_TOPALIGN, clientx, clienty, hwnd, NULL);
				return 0;
			}
		}
	}

	return 1;
}

static int onCreate(HWND, LPCREATESTRUCT)	// HWND hwnd, LPCREATESTRUCT lpCreateStruct
{
	return 1;
}

static void onActivateApp(HWND hwnd, BOOL activate, DWORD /* dwThreadId */)
{
	if (!kNetGame && autoPause && dialogManager.isEmpty()) {
		runPause = activate ? 0 : 1;
	}
	if (!activate && hwnd == hScrnWnd) {
		EndMenu();
	}
	if (!activate && runPause) {
		audioInterface.blank();
	}

	if (activate) {
		if (!dialogManager.isEmpty()) {
			inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		} else {
			gameInpCheckMouse();
		}
	}
}

static void onPaint(HWND hWnd)
{
	if (hWnd == hScrnWnd) {
		// paint and validate client area
		videoInterface.paint(1);

		// paint skin picture
		if (!driverOkay && !vidUsePlaceholder) {
			paintSkin(hWnd);
		}
	}
}

static void onClose(HWND)
{
	PostQuitMessage(0);					// Quit the program if the window is closed
}

static void onDestroy(HWND)
{
//	videoInterface.exit();				// Stop using video with the Window
	hScrnWnd = NULL;					// Make sure handle is not used again
}

void quickLoadFile()
{
	if (commonFileDialog(hScrnWnd, false, 0, L"All support types\0*.zip;*.7z;*.fr;*.fs\0" L"Archive File (*.zip,*.7z)\0*.zip;*.7z\0\0", 0, 0)) {
		return;
	}

	directLoadGame(commonChoice);
}

static void mountCartridge(bool mount)
{
	if (mount) {
		if (BurnCartridgeGameLoaded) {
			if (!commonFileDialog(hScrnWnd, false, 0, AtoW(pBurnCartridgeFileExtFilter)) && pBurnInsertCartridgeCallBack) {
				pBurnInsertCartridgeCallBack(WtoA(commonChoice));
			}
		}
	} else {
		if (BurnCartridgeGameLoaded && pBurnInsertCartridgeCallBack) {
			pBurnInsertCartridgeCallBack(0);
		}
	}
}

bool bLoading = false;

static void onCommand(HWND hDlg, int id, HWND /*hwndCtl*/, UINT codeNotify)
{
	if (bLoading) {
		return;
	}

	switch (id) {
		case MENU_LOAD:
			HK_openGame(0);
			break;

		case MENU_LOAD_QUICK:
			HK_quickOpenGame(0);
			break;

		case MENU_QUIT:
			HK_exitGame(0);
			break;

		case MENU_EXIT:
			stopReplay();
			aviStop();
			FBAKailleraEnd();
			PostQuitMessage(0);

			menuSync(MENUT_ALL);
			return;

		case MENU_CARTR_MOUNT:
			mountCartridge(true);
			menuSync(MENUT_FILE);
			break;

		case MENU_CARTR_UNMOUNT:
			mountCartridge(false);
			menuSync(MENUT_FILE);
			break;

		case MENU_STARTNET:
			if (!kNetGame) {
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
				audioInterface.blank();
				splashDestroy(1);
				stopReplay();
				aviStop();
				BurnerDrvExit();
				// load kaillera.dll
				if (FBAKailleraInit()) {
					doNetGame();
				}
				menuSync(MENUT_ALL);
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
			}
			break;

		case MENU_STARTREPLAY:
			HK_playRec(0);
			break;
		case MENU_STARTRECORD:
			HK_startRec(0);
			break;
		case MENU_STOPREPLAY:
			HK_stopRec(0);
			break;

		case MENU_AVISTART:
			HK_startAvi(0);
			break;
		case MENU_AVISTOP:
			HK_stopAvi(0);
			break;
		case MENU_AVIINTAUD:
			aviIntAudio = !aviIntAudio;
			menuSync(MENUT_FILE);
			break;

		case MENU_MEMCARD_CREATE:
			if (useDialogs() && driverOkay && !kNetGame
				&& (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
				audioInterface.blank();
				memCardEject();
				memCardCreate();
				memCardInsert();
				menuSync(MENUT_FILE);
				gameInpCheckMouse();
			}
			break;
		case MENU_MEMCARD_SELECT:
			if (useDialogs() && driverOkay && !kNetGame
				&& (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
				audioInterface.blank();
				memCardEject();
				memCardSelect();
				memCardInsert();
				menuSync(MENUT_FILE);
				gameInpCheckMouse();
			}
			break;
		case MENU_MEMCARD_INSERT:
			if (driverOkay && !kNetGame
				&& (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {
				memCardInsert();
			}
			break;
		case MENU_MEMCARD_EJECT:
			if (driverOkay && !kNetGame
				&& (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {
				memCardEject();
			}
			break;

		case MENU_MEMCARD_TOGGLE:
			if (driverOkay && !kNetGame
				&& (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {
				memCardToggle();
			}
			break;

		case MENU_STATE_LOAD_DIALOG:
			HK_loadStateDialog(0);
			break;
		case MENU_STATE_SAVE_DIALOG:
			HK_saveStateDialog(0);
			return;
		case MENU_STATE_PREVSLOT:
			HK_prevState(0);
			break;
		case MENU_STATE_NEXTSLOT:
			HK_nextState(0);
			break;
		case MENU_STATE_LOAD_SLOT:
			HK_loadCurState(0);
			break;
		case MENU_STATE_SAVE_SLOT:
			HK_saveCurState(0);
			break;

		case MENU_STATE_ALLRAM:
			drvSaveAll = !drvSaveAll;
			menuSync(MENUT_FILE);
			break;

		case MENU_WLOGSTART:
			audioInterface.blank();
			waveLogStart();
			break;
		case MENU_WLOGEND:
			audioInterface.blank();
			waveLogStop();
			break;

		case MENU_NOSTRETCH:
			vidCorrectAspect = 0;
			vidFullStretch = 0;
			simpleReinitScrn(false);
			menuSync(MENUT_SETTING);
			break;
		case MENU_STRETCH:
			vidFullStretch = 1;
			vidCorrectAspect = 0;
			simpleReinitScrn(false);
			menuSync(MENUT_SETTING);
			break;
		case MENU_ASPECT:
			vidCorrectAspect = 1;
			vidFullStretch = 0;
			simpleReinitScrn(false);
			menuSync(MENUT_SETTING);
			break;

		case MENU_FULL:
			HK_fullscreen(0);
			return;

		case MENU_WINDOWSIZE1X:
		case MENU_WINDOWSIZE2X:
		case MENU_WINDOWSIZE3X:
		case MENU_WINDOWSIZE4X:
		case MENU_WINDOWSIZE5X:
			HK_windowSize(id - MENU_WINDOWSIZE1X + 1);
			break;

		case MENU_HOTKEYS:
			MHkeysCreate(hDlg);
			break;

		case MENU_INPUT:
			HK_configPad(0);
			break;

		case MENU_DIPSW:
			HK_setDips(0);
			break;

		case MENU_SETCPUCLOCK:
			audioInterface.blank();
			cpuClockDialog(hDlg);
			menuSync(MENUT_GAME);
			gameInpCheckMouse();
			break;

		// ==> rom save
		case MENU_SAVEC:
			bsavedecryptedcs = !bsavedecryptedcs;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVEP:
			bsavedecryptedps = !bsavedecryptedps;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVES:
			bsavedecrypteds1 = !bsavedecrypteds1;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVEV:
			bsavedecryptedvs = !bsavedecryptedvs;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVEM:
			bsavedecryptedm1 = !bsavedecryptedm1;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVEXOR:
			bsavedecryptedxor = !bsavedecryptedxor;
			menuSync(MENUT_GAME);
			break;
		case MENU_SAVEDPROM:
			bsavedecryptedprom = !bsavedecryptedprom;
			menuSync(MENUT_GAME);
			break;
		// <== rom save

		case MENU_LANGUAGE_SELECT:
			if (useDialogs()) {
				if (!localiseLoad()) {
					menuReinit();
				}
			}
			break;
		case MENU_LANGUAGE_UNLOAD:
			wcsncpy(szLanguage, L"", sizearray(szLanguage));
			localiseExit();
			menuReinit();
			break;

		case MENU_GAMELIST_SELECT:
			if (useDialogs()) {
				loadGamelist();
			}
			break;
		case MENU_GAMELIST_EXPORT:
			if (useDialogs()) {
				createGamelist();
			}
			break;

		case MENU_MISCDIR_EDIT:
			if (useDialogs()) {
				pathSheetCreate(hDlg);
			}
			break;
		case MENU_ROMDIR_EDIT:
			if (useDialogs()) {
				romsDirCreate(hDlg);
			}
			break;

		case MENU_ENABLECHEAT:
			HK_cheatEditor(0);
			break;

		// ==> simple jukebox, added by regret
		case MENU_JUKEBOX:
			if (useDialogs()) {
				jukeCreate();
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
			}
			break;
		// <== simple jukebox

		case MENU_SAVESNAP: {
			HK_screenShot(0);
			break;
		}
		case MENU_SNAPFACT:
			HK_shotFactory(0);
			break;

		case MENU_SKIN_SELECT:
			if (useDialogs()) {
				if (selectSkin() == 0) {
					simpleReinitScrn(true);
				}
			}
			break;
		case MENU_SKIN_UNLOAD:
			wcsncpy(skinPlaceHolder, L"", sizearray(skinPlaceHolder));
			simpleReinitScrn(true);
			break;

		case MENU_CLRMAME_PRO:
			if (useDialogs()) {
				createDatfile();
			}
			break;

		case MENU_SAVESET:
			Config::save();
			break;
		case MENU_LOADSET:
			Config::load();
			mediaReInit();
			break;

		case MENU_ABOUT:
			if (useDialogs()) {
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
				audioInterface.blank();
				aboutCreate(hDlg);
				gameInpCheckMouse();
			}
			break;
		case MENU_SYSINFO:
			if (useDialogs()) {
				inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
				audioInterface.blank();
				systemInfoCreate(hDlg);
				gameInpCheckMouse();
			}
			break;

		case MENU_CONTENTS: {
			FILE* fp = _wfopen(L"readme.txt", L"r");
			if (fp) {
				fclose(fp);
				ShellExecute(NULL, L"open", L"readme.txt", NULL, NULL, SW_SHOWNORMAL);
			}
			break;
		}

		// filters
		case MENU_SOFT_NONE:
			filter.enable = false;
			filter.filterId = 0;
			videoInterface.reinit();
			menuSync(MENUT_SETTING);
			break;

		case MENU_SOFT_EPXB:
		case MENU_SOFT_EPXC:
		case MENU_SOFT_SCALE2X:
		case MENU_SOFT_SCALE3X:
		case MENU_SOFT_2XSAI:
		case MENU_SOFT_SUPER2XSAI:
		case MENU_SOFT_SUPEREAGLE:
		case MENU_SOFT_HQ2X:
		case MENU_SOFT_HQ2XS:
		case MENU_SOFT_HQ3X:
		case MENU_SOFT_HQ3XS:
			filter.enable = true;
			filter.filterId = id - MENU_SOFT_NONE;
			scrnSize();
			filter.select(filter.filterId);
			menuSync(MENUT_SETTING);
			break;

		// Options for blitters
		case MENU_FILTER_POINT:
		case MENU_FILTER_LINEAR:
			filter.linear = !filter.linear;
			videoInterface.set(L"Filter", 0);
			menuSync(MENUT_SETTING);
			break;

		case MENU_CONFIGALL:
			if (useDialogs()) {
				preferenceCreate(hDlg);
			}
			break;
		case MENU_VIDEOCONFIG:
			if (useDialogs()) {
				prefVideoCreate(hDlg);
				setWindowAspect();
				simpleReinitScrn(true);
			}
			break;
		case MENU_AUDIOCONFIG:
			if (useDialogs()) {
				prefAudioCreate(hDlg);
				mediaReInitAudio();
			}
			break;
		case MENU_MISCCONFIG:
			if (useDialogs()) {
				prefMiscCreate(hDlg);
				menuReinit();
				videoInterface.reinit();
			}
			break;

//		default:
//			printf("  * Command %i sent.\n");
	}
}

// Block screensaver and windows menu if needed
static int onSysCommand(HWND, UINT sysCommand, int, int)
{
	switch (sysCommand) {
		case SC_MONITORPOWER:
		case SC_SCREENSAVE: {
			if (!runPause && driverOkay) {
				return 1;
			}
			break;
		}
		case SC_KEYMENU:
		case SC_MOUSEMENU: {
			if (kNetGame) {
				return 1;
			}
			break;
		}
	}

	return 0;
}

static void onEnterIdle(HWND /*hwnd*/, UINT /*source*/, HWND /*hwndSource*/)
{
	MSG msg;

	// Modeless dialog is idle
	while (kNetGame && !PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
		runIdle();
	}
}

static void onSize(HWND, UINT state, int cx, int cy)
{
	if (state == SIZE_MINIMIZED) {
		return;
	}

	if (state == SIZE_MAXIMIZED || state == SIZE_RESTORED) {
		if (state == SIZE_MAXIMIZED) {
			videoInterface.clear();
		}

		if (!driverOkay || runPause) {
			videoInterface.redraw();
			videoInterface.paint(0);
		}
	}
}

static void onEnterSizeMove(HWND)
{
//	audioInterface.blank();

	RECT rect;
	GetClientRect(hScrnWnd, &rect);
	prevWidth = rect.right;
	prevHeight = rect.bottom;
}

static void onExitSizeMove(HWND)
{
	RECT rect;
	GetWindowRect(hScrnWnd, &rect);
	windowPosX = rect.left;
	windowPosY = rect.top;

	if (!driverOkay) {
		videoInterface.redraw();
		videoInterface.paint(0);
	}
}

// ==> set screen visible size, added by regret
static inline int getScrnExtraWidth()
{
	int ew = GetSystemMetrics(SM_CXSIZEFRAME) << 1;
	if (!menuEnabled) {
		ew += 1 << 1;
	}
	return ew;
}

static inline int getScrnExtraHeight()
{
	int eh = GetSystemMetrics(SM_CYSIZEFRAME) << 1;
	if (menuEnabled) {
		eh += GetSystemMetrics(SM_CYCAPTION);
		eh += GetSystemMetrics(SM_CYMENU);
	} else {
		eh += 1 << 1;
	}
	return eh;
}

static void onSizing(HWND, WPARAM wParam, LPARAM lParam)
{
	RECT* rect = (RECT *)lParam;
	int adjustment = wParam;
	int extrawidth = getScrnExtraWidth();
	int extraheight = getScrnExtraHeight();
	int adjwidth, adjheight;
	unsigned bmapWidth = videoInterface.getImageWidth();
	unsigned bmapHeight = videoInterface.getImageHeight();

	// get game size
	if (driverOkay) {
		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && !(vidRotationAdjust & 1)) {
			BurnDrvGetVisibleSize(bmapHeight, bmapWidth);
		} else {
			BurnDrvGetVisibleSize(bmapWidth, bmapHeight);
		}
	}

	RECT rect_image = {
		0, 0, rect->right-rect->left-extrawidth, rect->bottom-rect->top-extraheight
	};
	videoInterface.scale(&rect_image, bmapWidth, bmapHeight);

	// calculate screen size to fit game size
	adjwidth = rect->left + rect_image.right - rect_image.left + extrawidth - rect->right;
	adjheight = rect->top + rect_image.bottom - rect_image.top + extraheight - rect->bottom;

	switch (adjustment)
	{
		case WMSZ_BOTTOM:
		case WMSZ_BOTTOMRIGHT:
		case WMSZ_RIGHT:
			rect->right += adjwidth;
			rect->bottom += adjheight;
			break;

		case WMSZ_BOTTOMLEFT:
			rect->left -= adjwidth;
			rect->bottom += adjheight;
			break;

		case WMSZ_LEFT:
		case WMSZ_TOPLEFT:
		case WMSZ_TOP:
			rect->left -= adjwidth;
			rect->top -= adjheight;
			break;

		case WMSZ_TOPRIGHT:
			rect->right += adjwidth;
			rect->top -= adjheight;
			break;
	}
}
// <== set screen visible size

// ==> drag & drop support, added by regret
static void onDropFiles(HWND, WPARAM wParam)
{
	wchar_t filename[MAX_PATH];
	HDROP hDrop = (HDROP)wParam;
	DragQueryFile(hDrop, 0, filename, MAX_PATH); // get first filename
	DragFinish(hDrop);
	directLoadGame(filename);
}
// <== drag & drop support

static LRESULT CALLBACK scrnProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
		HANDLE_MSG(hWnd, WM_CREATE,			onCreate);
		HANDLE_MSG(hWnd, WM_ACTIVATEAPP,	onActivateApp);
		HANDLE_MSGB(hWnd,WM_PAINT,			onPaint);
		HANDLE_MSG(hWnd, WM_CLOSE,			onClose);
		HANDLE_MSG(hWnd, WM_DESTROY,		onDestroy);
		HANDLE_MSG(hWnd, WM_COMMAND,		onCommand);

		// We can't use the macro from windowsx.h macro for this one
		case WM_SYSCOMMAND: {
			if (onSysCommand(hWnd,(UINT)wParam,(int)(short)LOWORD(lParam),(int)(short)HIWORD(lParam))) {
				return 0;
			}
			break;
		}

		HANDLE_MSG(hWnd, WM_SIZE,			onSize);
		HANDLE_MSG(hWnd, WM_ENTERSIZEMOVE,	onEnterSizeMove);
		HANDLE_MSG(hWnd, WM_EXITSIZEMOVE,	onExitSizeMove);

		HANDLE_MSGB(hWnd, WM_SIZING,		onSizing);		// set screen visible size, added by regret
		HANDLE_MSGB(hWnd, WM_DROPFILES,		onDropFiles);	// drag & drop support, added by regret

		HANDLE_MSG(hWnd, WM_ENTERIDLE,		onEnterIdle);
		HANDLE_MSG(hWnd, WM_MOUSEMOVE,		onMouseMove);
		HANDLE_MSG(hWnd, WM_LBUTTONUP,		onLButtonUp);
		HANDLE_MSG(hWnd, WM_LBUTTONDOWN,	onLButtonDown);
		HANDLE_MSG(hWnd, WM_LBUTTONDBLCLK,	onLButtonDblClk);
//		HANDLE_MSG(hWnd, WM_RBUTTONUP,		onRButtonUp);
		HANDLE_MSG(hWnd, WM_RBUTTONDBLCLK,	onRButtonDown);
		HANDLE_MSG(hWnd, WM_RBUTTONDOWN,	onRButtonDown);

		HANDLE_MSG(hWnd, WM_DISPLAYCHANGE,	onDisplayChange);
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int scrnTitle()
{
	static wchar_t text[1024] = L"";

	// Create window title
	if (driverOkay) {
		const wchar_t* name = transGameName(BurnDrvGetTextA(DRV_FULLNAME));

		if (showFPS) {
			extern wchar_t fpsString[];
			_snwprintf(text, sizearray(text), L"%s [%s]", name, fpsString);
		} else {
			swprintf(text, L"%s", name);
		}
	} else {
		swprintf(text, APP_TITLE L" v%.20s", appBurnVer);
	}

	SetWindowText(hScrnWnd, text);
	return 0;
}

int scrnSize()
{
	if (!hScrnWnd || vidFullscreen || fakeFullscreen) {
		return 1;
	}

	dragX = GetSystemMetrics(SM_CXDRAG) / 2;
	dragY = GetSystemMetrics(SM_CYDRAG) / 2;

	// Find the size of the visible WorkArea
	SystemParametersInfo(SPI_GETWORKAREA, 0, &systemWorkArea, 0);

	int scrnWidth = systemWorkArea.right - systemWorkArea.left;
	int scrnHeight = systemWorkArea.bottom - systemWorkArea.top;
	double winAspect = (double)scrnWidth / (double)scrnHeight;

	int maxSize = scrnWindowSize;
	if (maxSize <= 0) {
		maxSize = 1;
	}

	// Find the width and height
	int w = scrnWidth;
	int h = scrnHeight;

	// Find out how much space is taken up by the borders
	int ew = getScrnExtraWidth();
	int eh = getScrnExtraHeight();

	if (menuEnabled) {
		// Subtract the border space
		w -= ew;
		h -= eh;
	}

	unsigned bmapWidth = videoInterface.getImageWidth();
	unsigned bmapHeight = videoInterface.getImageHeight();
	// adjust the screen size for skin, added by regret
	if (!driverOkay) {
		if (bmapWidth > DEFAULT_IMAGE_WIDTH) {
			bmapWidth = DEFAULT_IMAGE_WIDTH;
		}
		if (bmapHeight > DEFAULT_IMAGE_HEIGHT) {
			bmapHeight = DEFAULT_IMAGE_HEIGHT;
		}
	} else {
		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && !(vidRotationAdjust & 1)) {
			BurnDrvGetVisibleSize(bmapHeight, bmapWidth);
		} else {
			BurnDrvGetVisibleSize(bmapWidth, bmapHeight);
		}
	}

	videoInterface.calcGameAspect(); // calc game aspect

	// window size
	if (vidCorrectAspect || vidFullStretch) {
		int ww = w;
		int hh = h;

		do {
			if (bmapWidth < bmapHeight) {
				if (ww > bmapWidth * maxSize) {
					ww = bmapWidth * maxSize;
				}

				int sizeh = ww * vidScrnAspect / (winAspect * videoInterface.getGameAspect());
				if (hh > sizeh) {
					hh = sizeh;
				}
			} else {
				if (hh > bmapHeight * maxSize) {
					hh = bmapHeight * maxSize;
				}

				int sizew = hh * videoInterface.getGameAspect() * winAspect / vidScrnAspect;
				if (ww > sizew) {
					ww = sizew;
				}
			}
		} while ((ww > w || hh > h) && maxSize-- > 1);
		w =	ww;
		h = hh;
	} else {
		while ((bmapWidth * maxSize > w || bmapHeight * maxSize > h) && maxSize > 1) {
			maxSize--;
		}

		if (w > bmapWidth * maxSize || h > bmapHeight * maxSize) {
			w = bmapWidth * maxSize;
			h = bmapHeight * maxSize;
		}
	}

	RECT rect = { 0, 0, w, h };
	videoInterface.scale(&rect, bmapWidth, bmapHeight);
	w = rect.right - rect.left + ew;
	h = rect.bottom - rect.top + eh;

	int x = windowPosX;
	int y = windowPosY;
	if (x + w > systemWorkArea.right || y + h > systemWorkArea.bottom) {
		// Find the midpoint for the window
		x = systemWorkArea.left + systemWorkArea.right;
		x /= 2;
		y = systemWorkArea.bottom + systemWorkArea.top;
		y /= 2;

		x -= w / 2;
		y -= h / 2;
	}

	winMaximised = false;

	MoveWindow(hScrnWnd, x, y, w, h, TRUE);
//	SetWindowPos(hScrnWnd, NULL, x, y, w, h, SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOZORDER);
	if (driverOkay && showOnTop) {
		SetWindowPos(hScrnWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);
	}

	windowPosX = x; windowPosY = y;

  	return 0;
}

// Init the screen window (create it)
static int scrnRegister()
{
	WNDCLASSEX WndClassEx;
	ATOM Atom = 0;

	// Register the window class
	memset(&WndClassEx, 0, sizeof(WndClassEx)); 		// Init structure to all zeros
	WndClassEx.cbSize = sizeof(WndClassEx);
	WndClassEx.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;// These cause flicker in the toolbar
	WndClassEx.lpfnWndProc = scrnProc;
	WndClassEx.hInstance = hAppInst;
	WndClassEx.hIcon = LoadIcon(hAppInst, MAKEINTRESOURCE(IDI_APP));
	WndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
	WndClassEx.hbrBackground = CreateSolidBrush(0);
	WndClassEx.lpszClassName = szClass;

	// Register the window class with the above information:
	Atom = RegisterClassEx(&WndClassEx);
	if (!Atom) {
		return 1;
	}
	return 0;
}

int scrnInit()
{
	if (hScrnWnd) {
		return 0;
	}

	if (scrnRegister() != 0) {
		FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_WINDOW));
		FBAPopupDisplay(PUF_TYPE_ERROR);
		return 1;
	}

	DWORD windowStyles, windowExStyles;

	if (vidFullscreen) {
		windowStyles = WS_POPUP;
		windowExStyles = 0;
	} else {
		if (menuEnabled) {
			windowStyles = WS_OVERLAPPEDWINDOW;
			windowExStyles = 0;
		} else {
			windowStyles = WS_MINIMIZEBOX | WS_POPUP | WS_SYSMENU | WS_THICKFRAME;
			windowExStyles = WS_EX_CLIENTEDGE;
		}
	}

	hScrnWnd = CreateWindowEx(windowExStyles, szClass, APP_TITLE, windowStyles,
		0, 0, 0, 0,
		NULL, NULL, hAppInst, NULL);

	if (hScrnWnd == NULL) {
		scrnExit();
		FBAPopupAddText(PUF_TEXT_DEFAULT, MAKEINTRESOURCE(IDS_ERR_UI_WINDOW));
		FBAPopupDisplay(PUF_TYPE_ERROR);
		return 1;
	}

	if (!vidFullscreen) {
		// enable drag & drop
		DragAcceptFiles(hScrnWnd, TRUE);

		// Create the menu toolbar itself
		menuCreate();

		scrnTitle();
		scrnSize();
	}

	ShowWindow(hScrnWnd, appShowCmd);		// Show the screen window
	appShowCmd = SW_NORMAL;
	SetForegroundWindow(hScrnWnd);

	return 0;
}

// Exit the screen window (destroy it)
int scrnExit()
{
	if (hScrnWnd) {
		DestroyWindow(hScrnWnd);
		hScrnWnd = NULL;
	}

	UnregisterClass(szClass, hAppInst);		// Unregister the scrn class

	menuDestroy();

	return 0;
}

// ==> switch fullscreen, added by regret
int scrnSwitchFull()
{
	if (!hScrnWnd) {
		return 1;
	}

	DWORD styles, exStyles;

	if (vidFullscreen) {
		styles = WS_POPUP;
		exStyles = 0;
		SetWindowLongPtr(hScrnWnd, GWL_STYLE, styles);
		SetWindowLongPtr(hScrnWnd, GWL_EXSTYLE, exStyles);

		SetMenu(hScrnWnd, NULL);
		fakeFullscreen = false;
	} else {
		if (menuEnabled) {
			styles = WS_OVERLAPPEDWINDOW;
			exStyles = 0;
		} else {
			styles = WS_MINIMIZEBOX | WS_POPUP | WS_SYSMENU | WS_THICKFRAME;
			exStyles = WS_EX_CLIENTEDGE;
		}
		SetWindowLongPtr(hScrnWnd, GWL_STYLE, styles);
		SetWindowLongPtr(hScrnWnd, GWL_EXSTYLE, exStyles);

		menuCreate();
		if (menuEnabled) {
			SetMenu(hScrnWnd, hMainMenu);
		} else {
			SetMenu(hScrnWnd, NULL);
		}

		scrnTitle();
		scrnSize();

		ShowWindow(hScrnWnd, SW_NORMAL);
	}

	videoInterface.reinit();

	gameInpCheckMouse();

	return 0;
}

// fake fullscreen
int scrnFakeFullscreen()
{
	if (!hScrnWnd) {
		return 1;
	}
	if (vidFullscreen) {
		return 0;
	}

	fakeFullscreen = !fakeFullscreen;

	DWORD styles, exStyles;

	if (fakeFullscreen) {
		styles = WS_POPUP;
		exStyles = 0;
		SetWindowLongPtr(hScrnWnd, GWL_STYLE, styles);
		SetWindowLongPtr(hScrnWnd, GWL_EXSTYLE, exStyles);
		SetMenu(hScrnWnd, NULL);

		// set screen size
		int width = GetSystemMetrics(SM_CXSCREEN);
		int height = GetSystemMetrics(SM_CYSCREEN);
		MoveWindow(hScrnWnd, 0, 0, width, height, TRUE);
		if (driverOkay && showOnTop) {
			SetWindowPos(hScrnWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);
		}

		// hide the cursor
//		while (ShowCursor(FALSE) >= 0) {}

		ShowWindow(hScrnWnd, SW_NORMAL);
	} else {
		if (menuEnabled) {
			styles = WS_OVERLAPPEDWINDOW;
			exStyles = 0;
		} else {
			styles = WS_MINIMIZEBOX | WS_POPUP | WS_SYSMENU | WS_THICKFRAME;
			exStyles = WS_EX_CLIENTEDGE;
		}
		SetWindowLongPtr(hScrnWnd, GWL_STYLE, styles);
		SetWindowLongPtr(hScrnWnd, GWL_EXSTYLE, exStyles);

		menuCreate();
		if (menuEnabled) {
			SetMenu(hScrnWnd, hMainMenu);
		} else {
			SetMenu(hScrnWnd, NULL);
		}

		scrnTitle();
		scrnSize();

		ShowWindow(hScrnWnd, SW_MAXIMIZE); // refresh
		ShowWindow(hScrnWnd, SW_NORMAL);

		// show the cursor
//		while (ShowCursor(TRUE) < 0) {}
	}

	videoInterface.clear();

	return 0;
}

// set fullscreen (for opengl)
int scrnSetFull(bool full)
{
	if (full) {
		DISPLAY_DEVICE dev;
		memset(&dev, 0, sizeof(dev));
		dev.cb = sizeof(dev);
		EnumDisplayDevices(NULL, vidAdapter, &dev, 0);

		DEVMODE mode;
		memset(&mode, 0, sizeof(mode));
		mode.dmSize = sizeof(mode);
		mode.dmBitsPerPel = vidScrnDepth;
		mode.dmPelsWidth = vidScrnWidth;
		mode.dmPelsHeight = vidScrnHeight;
		mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		LONG ret = ChangeDisplaySettingsEx(dev.DeviceName, &mode, NULL, CDS_FULLSCREEN, NULL);
		if (ret != DISP_CHANGE_SUCCESSFUL) {
			return 1;
		}

		SetWindowPos(hScrnWnd, NULL, 0, 0, vidScrnWidth, vidScrnHeight, SWP_NOZORDER | SWP_NOACTIVATE);
//		MoveWindow(hScrnWnd, 0, 0, vidScrnWidth, vidScrnHeight, FALSE);

		ShowWindow(hScrnWnd, SW_NORMAL);
		fakeFullscreen = false;
	} else {
		ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
	}

	return 0;
}
// <== switch fullscreen

void __cdecl scrnReinit()
{
	simpleReinitScrn(true);
}
